﻿using System;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.VCProject;
using Microsoft.VisualStudio.VCProjectEngine;
using Microsoft.VisualStudio.Shell.Interop;
using System.Reflection;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace HandOfSorrow.VsHelper
{
    class VCProjectHelper
    {
        public void ConfiguringVCProject(IVsHierarchy hierarchy)
        {
            VCProject vcprj = GetProject(hierarchy).Object as VCProject;
            if (null == vcprj)
                return;

            foreach (VCConfiguration vc_cfg in vcprj.Configurations as IVCCollection)
            {
                ConfiguringVCConfiguration(vc_cfg);
            }

       //     ListenVCProjectEvent(vcprj);
        }
        private void ConfiguringVCConfiguration(VCConfiguration vc_cfg)
        {
            VCPropertySheet vc_prop = AddVCPropertySheetToProject(vc_cfg);
            if (null != vc_prop)
                ConfiguringVCPropertySheet(vc_prop);

            ConfiguringVCCLCompilerTool(vc_cfg, vc_prop);

            switch (vc_cfg.ConfigurationType)
            {
            case Microsoft.VisualStudio.VCProjectEngine.ConfigurationTypes.typeDynamicLibrary:
                {

                    SetUserMacro(vc_prop, "OUT_FILE_EXT", ".dll");
                    break;
                }
            case Microsoft.VisualStudio.VCProjectEngine.ConfigurationTypes.typeApplication:
                {
                    ConfiguringVCLinkerTool(vc_cfg, vc_prop);
                    SetUserMacro(vc_prop, "OUT_FILE_EXT", ".exe");
                    SetUserMacro(vc_prop, "OUT_FILE_NAME", "$(ProjectName)$(OUT_FILE_EXT)");
                    break;
                }
            case Microsoft.VisualStudio.VCProjectEngine.ConfigurationTypes.typeStaticLibrary:
                {
                    SetUserMacro(vc_prop, "LINK_OPT", "-static");
                    SetUserMacro(vc_prop, "OUT_FILE_EXT", ".lib");
                    break;
                }
            }
            vc_prop.Save();
        }
        public void ListenVCProjectEvent(VCProject vcprj)
        {
            try
            {
                VCProjectEngine engine = vcprj.VCProjectEngine as VCProjectEngine;
                VCProjectEngineEvents events = engine.Events as VCProjectEngineEvents;
                events.ItemPropertyChange += new _dispVCProjectEngineEvents_ItemPropertyChangeEventHandler(VCProjectEngineEvents_ItemPropertyChangeEventHandler);
            }
            catch
            {
                MessageBox.Show("VCProjectEngine events could not be registered.");
            }

        }
        private EnvDTE.Project GetProject(IVsHierarchy hierarchy)
        {
            object project;
            hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out project);
            return (project as EnvDTE.Project);
        }
        private void WritePropertyFile(string path)
        {
            Assembly asm = Assembly.GetExecutingAssembly();
            Stream s = asm.GetManifestResourceStream("HandOfSorrow.VsHelper.Resources.VsHelper.props");
            if (null == s)
                return;

            byte[] bytes = new byte[s.Length];
            if (s.Length > int.MaxValue)
            {
                s.Write(bytes, 0, int.MaxValue);
                s.Write(bytes, int.MaxValue, (int)(s.Length - int.MaxValue));
            }
            else
            {
                s.Read(bytes, 0, (int)s.Length);
            }
            Directory.CreateDirectory(path);
            File.WriteAllBytes(path + "\\VsHelper.props", bytes);
            s.Close();
        }
        private VCPropertySheet AddVCPropertySheetToProject(VCConfiguration vc_cfg)
        {
            VCProject vc_prj = vc_cfg.project as VCProject;

            string path = "detail" + "\\" + vc_cfg.ConfigurationName;
            WritePropertyFile(vc_prj.ProjectDirectory + path);
            string prop_name = "detail\\" + vc_cfg.ConfigurationName + "\\VsHelper.props";
            vc_cfg.InheritedPropertySheets += ";.\\" + prop_name;
            IVCCollection props = vc_cfg.PropertySheets as IVCCollection;
            return props.Item("VsHelper") as VCPropertySheet;
        }
        private void ConfiguringVCPropertySheet(VCPropertySheet vc_prop)
        {
            SetUserMacro(vc_prop, "LIB_DIR", "lib\\");
            SetUserMacro(vc_prop, "BIN_DIR", "bin\\");
            SetUserMacro(vc_prop, "PDB_DIR", "pdb\\");
            SetUserMacro(vc_prop, "BUILD_DIR", "build\\");

            SetUserMacro(vc_prop, "OUT_FILE_NAME", "$(ProjectName)$(VC_TOOLSET)$(THREAD_OPT)$(RT_OPT)$(LINK_OPT)$(OUT_FILE_EXT)");

            SetUserMacro(vc_prop, "RT_OPT", null);              // -sd -s -d or null
            SetUserMacro(vc_prop, "LINK_OPT", null);            // static or null
            SetUserMacro(vc_prop, "THREAD_OPT", null);          // -mt or null

            SetUserMacro(vc_prop, "VC_TOOLSET", "-vc90");
        }
        private void SetUserMacro(VCPropertySheet prop, string name, string value)
        {
            if (null != prop)
            {
                foreach (VCUserMacro macro in prop.UserMacros)
                {
                    if (macro.Name == name)
                    {
                        macro.Value = value;
                        return;
                    }
                }
                prop.AddUserMacro(name, value);
            }
        }
        private void ConfiguringVCCLCompilerTool(VCConfiguration vc_cfg, VCPropertySheet vc_prop)
        {
            IVCCollection vctools = vc_cfg.Tools as IVCCollection;

            VCCLCompilerTool vcclt = vctools.Item("VCCLCompilerTool") as VCCLCompilerTool;
            
            string rt_opt = "";
            switch (vcclt.RuntimeLibrary)
            {
            case runtimeLibraryOption.rtMultiThreaded:
            case runtimeLibraryOption.rtMultiThreadedDebug:
                {
                    SetUserMacro(vc_prop, "THREAD_OPT", "-mt");
                    rt_opt += "-s";
                    break;
                }
            case runtimeLibraryOption.rtMultiThreadedDebugDLL:
            case runtimeLibraryOption.rtMultiThreadedDLL:
                {
                    SetUserMacro(vc_prop, "THREAD_OPT", "-mt");
                    break;
                }
            }
            string pp = vcclt.PreprocessorDefinitions;
            if (-1 != pp.IndexOf("_DEBUG"))
            {
                if ("" == rt_opt)
                    rt_opt = "-";

                rt_opt += "gd";
            }
            SetUserMacro(vc_prop, "RT_OPT", rt_opt);
        }
        private void ConfiguringVCLinkerTool(VCConfiguration vc_cfg, VCPropertySheet vc_prop)
        {
            IVCCollection vctools = vc_cfg.Tools as IVCCollection;

            VCLinkerTool vclt = vctools.Item("VCLinkerTool") as VCLinkerTool;
            vclt.OutputFile = "$(OutDir)$(OUT_FILE_NAME)";
            vc_cfg.OutputDirectory = "$(SolutionDir)$(BIN_DIR)";
        }
        private void ConfiguringVCLibrarianTool(VCConfiguration vc_cfg, VCPropertySheet vc_prop)
        {
            IVCCollection vctools = vc_cfg.Tools as IVCCollection;
            VCLibrarianTool vclibt = vctools.Item("VCLibrarianTool") as VCLibrarianTool;

        }

        private void VCProjectEngineEvents_ItemPropertyChangeEventHandler(Object item, Object Tool, int dispid)
        {
            int dispId_VCConfiguration_ConfigurationType = GetPropertyDispId(typeof(VCFileConfiguration), "ConfigurationType");
            int dispId_VCFileConfiguration_ExcludedFromBuild = GetPropertyDispId(typeof(VCFileConfiguration), "ExcludedFromBuild");

            int dispId_VCCLCompilerTool_RuntimeLibrary = GetPropertyDispId(typeof(VCCLCompilerTool), "RuntimeLibrary");

            int dispId_VCCLCompilerTool_UsePrecompiledHeader = GetPropertyDispId(typeof(VCCLCompilerTool), "UsePrecompiledHeader");
            int dispId_VCCLCompilerTool_PrecompiledHeaderThrough = GetPropertyDispId(typeof(VCCLCompilerTool), "PrecompiledHeaderThrough");
            int dispId_VCCLCompilerTool_PreprocessorDefinitions = GetPropertyDispId(typeof(VCCLCompilerTool), "PreprocessorDefinitions");
            int dispId_VCCLCompilerTool_AdditionalIncludeDirectories = GetPropertyDispId(typeof(VCCLCompilerTool), "AdditionalIncludeDirectories");
            VCFileConfiguration vcFileCfg = item as VCFileConfiguration;
            if (null == vcFileCfg)
            {
                VCConfiguration vcCfg = item as VCConfiguration;
                if (vcCfg == null)
                    return;
                VCProject vcPrj = vcCfg.project as VCProject;
                if (vcPrj == null)
                    return;

                if (dispid == dispId_VCCLCompilerTool_UsePrecompiledHeader
                    || dispid == dispId_VCCLCompilerTool_PrecompiledHeaderThrough
                    || dispid == dispId_VCCLCompilerTool_AdditionalIncludeDirectories
                    || dispid == dispId_VCCLCompilerTool_PreprocessorDefinitions)
                {

                }
            }

        }
        private static int GetPropertyDispId(Type type, string propertyName)
        {
            Array ppi = type.GetProperties() as Array;
            foreach (PropertyInfo pi in ppi)
            {
       //         Trace.WriteLine(pi.PropertyType.Name);
                foreach (Attribute attribute in pi.GetCustomAttributes(true))
                {
                    DispIdAttribute dispIdAttribute = attribute as DispIdAttribute;
                    if (dispIdAttribute != null)
                    {
                       
                    }
                }
            }
//             System.Reflection.PropertyInfo pi = type.GetProperty(propertyName);
//             if (pi != null)
//             {
//                 foreach (Attribute attribute in pi.GetCustomAttributes(true))
//                 {
//                     DispIdAttribute dispIdAttribute = attribute as DispIdAttribute;
//                     if (dispIdAttribute != null)
//                     {
//                         return dispIdAttribute.Value;
//                     }
//                 }
//             }
            return 0;
        }
    }
}